using System;
using System.Globalization;
using System.Runtime.Remoting.Messaging;
using System.Security.Permissions;

namespace MSTestExtensions
{
    /// <remarks />
    public class ExpectedExceptionMessageAspect : TestAspect<ExpectedExceptionMessageAttribute>, IMessageSink, ITestAspect
    {
        #region Fields

        /// <remarks />
        private IMessageSink _nextSink;

        #endregion // Fields

        #region IMessageSink Members

        /// <remarks />
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
        public IMessage SyncProcessMessage(IMessage msg)
        {
            if (msg == null)
                throw new ArgumentNullException("msg");
            ExpectedExceptionMessageAttribute attribute = GetAttribute(msg);
            IMessage returnMethod;
            if (attribute != null)
            {
                returnMethod = _nextSink.SyncProcessMessage(msg);
                ReturnMessage returnMessage = returnMethod as ReturnMessage;
                if (returnMessage != null)
                {
                    string actualExceptionAssemblyQualifiedName = returnMessage.Exception.GetType().AssemblyQualifiedName;
                    string expectedExceptionAssemblyQualifiedName = attribute.ExceptionType.AssemblyQualifiedName;
                    if (actualExceptionAssemblyQualifiedName != expectedExceptionAssemblyQualifiedName)
                    {
                        string exceptionMessage = string.Format(CultureInfo.CurrentCulture, "The type of exception thrown ('{0}') did not match the type expected ('{1}').", actualExceptionAssemblyQualifiedName, expectedExceptionAssemblyQualifiedName);
                        throw new MSTestExtensionsException(exceptionMessage);
                    }
                    if (returnMessage.Exception.Message != attribute.ExceptionMessage)
                    {
                        string exceptionMessage = string.Format(CultureInfo.CurrentCulture, "The exception message thrown ('{0}') did not match the message expected ('{1}').", returnMessage.Exception.Message, attribute.ExceptionMessage);
                        throw new MSTestExtensionsException(exceptionMessage);
                    }
                    returnMethod = FakeTargetResponse(msg);
                }
            }
            else
            {
                returnMethod = _nextSink.SyncProcessMessage(msg);
            }
            return returnMethod;
        }

        /// <remarks />
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            throw new InvalidOperationException();
        }

        /// <remarks />
        public IMessageSink NextSink
        {
            [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
            get { return _nextSink; }
        }

        #endregion // IMessageSink Members

        #region ITestAspect

        /// <remarks />
        public void AddMessageSink(IMessageSink messageSink)
        {
            _nextSink = messageSink;
        }

        #endregion // ITestAspect
    }
}
